<?php
declare(strict_types=1);

function _include($lastname)
{
    static $len;
    $len = $len ? $len : strlen(dirname(__ROTDIR__));
    $tmpdir = __TMPDIR__ .'code/'. (isset($_ENV['bizan'])? $_ENV['bizan']->http_server->worker_id:666666);
    !is_dir($tmpdir) && mkdir($tmpdir,0777,true) && chmod($tmpdir,0777);
    $dumpfile = $tmpdir .'/'. substr(str_replace('/', '_', $lastname), $len+1);
    if (!is_file($lastname)) {
        $dumpfile=__TMPDIR__ . 'code/ErrorView.NoFile.php';
        if(!is_file($dumpfile)){
            $file_data=file_get_contents_try(__APPDIR__ . 'ErrorView/NoFile.php');
            file_put_contents_try($dumpfile,$file_data);
        }
        return $dumpfile;
    }

    if (!is_file($dumpfile) || _CONF('debug')) {
        $s = plugin_compile_srcfile($lastname);
        if (empty($s)) {
            return $dumpfile;
        } else {
            file_put_contents_try($dumpfile, $s);
        }
    }
    return $dumpfile;
}

/**
 * 文件丢到内存虚拟盘然后从内存读
 * @param $src_file
 * @return false|string
 */
function _file_read($src_file): false|string
{
    static $len;
    $len = $len ? $len : strlen(dirname(__ROTDIR__));
    $tmp_file = __TMPDIR__ . 'code/' . substr(str_replace('/', '_', $src_file), $len);
    if (!is_file($tmp_file) || _CONF('debug')) {
        $s = file_read($src_file);
        file_put_contents_try($tmp_file, $s);
    } else {
        $s = file_read($tmp_file);
    }
    return $s;
}


// 在安装、卸载插件的时候，需要先初始化
function plugin_init()
{
    $plugin_paths = glob(__PLUDIR__ . '*', GLOB_ONLYDIR);
    if (is_array($plugin_paths)) {
        $plugins = [];
        foreach ($plugin_paths as $path) {
            $dir = file_name($path);
            $config = $path . "/conf.json";
            if (!is_file($config)) continue;
            $arr = xn_json_decode(file_get_contents_try($config));
            if (empty($arr)) continue;
            !isset($arr['rank']) and $arr['rank'] = 0;
            empty($arr['cate']) && $arr['cate'] = [];

            $plugins[$dir] = $arr;
            // 额外的信息
            $plugins[$dir]['hooks'] = [];
            $hook_paths = glob(__PLUDIR__ . "$dir/hook/*.*"); // path
            if (is_array($hook_paths)) {
                foreach ($hook_paths as $hook_path) {
                    $hook_name = file_name($hook_path);
                    $plugins[$dir]['hooks'][$hook_name] = $hook_path;
                }
            }
            // 本地 + 线上数据
            $plugins[$dir] = plugin_read_by_dir($dir, $plugins[$dir]);
        }

        $plugins = arrlist_sort_by_many_field($plugins, 'rank', SORT_DESC);
        $_ENV['plugins'] = $plugins;
        //arrlist_multisort($plugins,'rank',FALSE);
    }
}

// 插件依赖检测，返回依赖的插件列表，如果返回为空则表示不依赖
/*
	返回依赖的插件数组：
	array(
		'xn_ad'=>'1.0',
		'xn_umeditor'=>'1.0',
	);
*/
function plugin_dependencies(string $dir): array
{
    $plugin = $_ENV['plugins'][$dir];
    $dependencies = $plugin['dependencies'];
    // 检查插件依赖关系
    $arr = [];
    foreach ($dependencies as $_dir => $version) {
        $_dir = ucfirst($_dir);
        if (!isset($_ENV['plugins'][$_dir]) || empty($_ENV['plugins'][$_dir]['enable'])) {
            $arr[$_dir] = $version;
        }
    }
    return $arr;
}

/*
	返回被依赖的插件数组：
	array(
		'xn_ad'=>'1.0',
		'xn_umeditor'=>'1.0',
	);
*/
function plugin_by_dependencies(string $dir): array
{
    $plugins = $_ENV['plugins'];
    $arr = [];
    foreach ($plugins as $_dir => $plugin) {
        if (isset($plugin['dependencies'][$dir]) && $plugin['enable']) {
            $arr[$_dir] = $plugin['version'];
        }
    }
    return $arr;
}

function plugin_enable(string $dir): bool
{
    $plugins = $_ENV['plugins'];
    if (!isset($plugins[$dir])) {
        return FALSE;
    }
    $plugins[$dir]['enable'] = 1;
    file_replace_var(__PLUDIR__ . $dir . '/conf.json', ['enable' => 1], TRUE);
    plugin_clear_tmp_dir();
    return TRUE;
}


function plugin_check_dependency(string $dir, string $action = 'install'): bool|array
{
    //$plugins = $_ENV['plugins'];
    if ($action == 'install' || $action == 'enable') {
        $arr = plugin_dependencies($dir);
        if (!empty($arr)) {
            $links = plugin_dependency_arr_to_links($arr);
            return $links ? $links : true;
        }
    } else {
        $arr = plugin_by_dependencies($dir);
        if (!empty($arr)) {
            $links = plugin_dependency_arr_to_links($arr);
            return $links ? $links : true;
        }
    }
    return true;
}

function plugin_dependency_arr_to_links(array $arr): array
{
    $plugins = $_ENV['plugins'];
    $array = [];
    foreach ($arr as $dir => $version) {
        $dir = ucfirst($dir);
        if (!empty($plugins[$dir]['enable']) && $plugins[$dir]['version_int'] < $version) continue;
        $array[] = $plugins[$dir] ?? ['name' => $dir, 'version' => $version, 'dir' => $dir];
    }
    return $array;
}

// 清空插件的临时目录
function plugin_clear_tmp_dir(): void
{
    rmdir_recursive(__TMPDIR__ . 'code/');
}

function plugin_disable(string $dir): bool
{
    $plugins = $_ENV['plugins'];
    if (!isset($plugins[$dir])) {
        return FALSE;
    }
    $plugins[$dir]['enable'] = 0;
    file_replace_var(__PLUDIR__ . $dir . '/conf.json', ['enable' => 0], TRUE);
    plugin_clear_tmp_dir();
    return TRUE;
}

// 安装所有的本地插件
function plugin_install_all(): void
{
    $plugins = $_ENV['plugins'];
    foreach ($plugins as $dir => $plugin) {
        plugin_install($dir);
    }
}

// 卸载所有的本地插件
function plugin_unstall_all(): void
{
    $plugins = $_ENV['plugins'];
    foreach ($plugins as $dir => $plugin) {
        plugin_unstall($dir);
    }
}

/*
	插件安装：
		把所有的插件点合并，重新写入文件。如果没有备份文件，则备份一份。
		插件名可以为源文件名：view/header.htm
*/
function plugin_install(string $dir): bool
{
    $plugins = $_ENV['plugins'];
    if (!isset($plugins[$dir])) {
        return FALSE;
    }

    $plugins[$dir]['installed'] = 1;
    $plugins[$dir]['enable'] = 1;

    // 写入配置文件
    file_replace_var(__PLUDIR__ . $dir . '/conf.json', ['installed' => 1, 'enable' => 1], TRUE);
    plugin_clear_tmp_dir();
    return TRUE;
}

// copy from plugin_install 修改
function plugin_unstall(string $dir): bool
{
    $plugins = $_ENV['plugins'];
    if (!isset($plugins[$dir])) {
        return FALSE;
    }

    $plugins[$dir]['installed'] = 0;
    $plugins[$dir]['enable'] = 0;

    // 写入配置文件
    file_replace_var(__PLUDIR__ . $dir . '/conf.json', ['installed' => 0, 'enable' => 0], TRUE);
    plugin_clear_tmp_dir();
    return TRUE;
}

function plugin_paths_enabled(): array
{
    $return_paths = [];
    $plugin_paths = glob(__PLUDIR__ . '*', GLOB_ONLYDIR);
    if (empty($plugin_paths)) return [];
    foreach ($plugin_paths as $path) {
        $conf_file = $path . "/conf.json";
        if (!is_file($conf_file)) continue;
        $conf = xn_json_decode(file_read($conf_file));
        if (empty($conf)) continue;
        if (empty($conf['enable']) || empty($conf['installed'])) continue;
        $conf['dir'] = str_replace(dirname($path) . '/', '', $path);
        $return_paths[$path] = $conf;
    }

    $_ENV['plugins_enable'] = arrlist_values($return_paths, 'dir');
    //$return_paths=arrlist_multisort($return_paths,'rank',false);
    return $return_paths;
}

// 编译源文件，把插件合并到该文件，不需要递归，执行的过程中 include _include() 自动会递归。
function plugin_compile_srcfile(string $srcfile): ?string
{
    // 判断是否开启插件
    $s = file_read($srcfile);
    if (!empty(_CONF('plugins_enable'))) {
        return $s;
    }
    //$s = file_read($srcfile);
    // 最多支持 10 层
    if ($s) {
        for ($i = 0; $i < 10; $i++) {
            if (strpos($s, '<!--{hook') !== FALSE || strpos($s, '<!--{inc') !== FALSE || strpos($s, '// hook') !== FALSE || strpos($s, '// replace') !== FALSE) {
                $s = preg_replace('#<!--{hook\s+(.*?)}-->#', '// hook \\1', $s);
                $s = preg_replace_callback('#//\s*hook\s+(\S+)#is', 'plugin_compile_srcfile_callback', $s);
                $s = preg_replace_callback('#//\s*make\s+(\S+)\s+(\S+)\s+(\S+)#is', 'plugin_compile_makefile_callback', $s);
                $s = preg_replace_callback('#//\s*replace\s+(\S+)#is', 'plugin_compile_replace_callback', $s);
                $s = preg_replace_callback('#<!--{inc\s+(.*?)}-->#is', 'compile_inc_callback', $s);

            } else {
                break;
            }
        }
    }
    return $s;
}

function plugin_get_hook()
{
    $hooks = [];
    $plugin_paths = plugin_paths_enabled();
    foreach ($plugin_paths as $path => $conf) {
        $dir = file_name($path);
        $hookpaths = glob(__PLUDIR__ . $dir . '/hook/*.*'); // path
        if (is_array($hookpaths)) {
            foreach ($hookpaths as $hookpath) {
                $hookname = file_name($hookpath);
                $rank = isset($conf['hooks_rank'][$hookname]) ? $conf['hooks_rank'][$hookname] : 999;
                $hooks[$hookname][] = ['hookpath' => $hookpath, 'rank' => $rank];
            }
        }
    }
    foreach ($hooks as $key => $arraylist) {
        $arraylist = arrlist_sort_by_many_field($arraylist, 'rank', SORT_DESC);
        $hooks[$key] = arrlist_values($arraylist, 'hookpath');
    }
    $_ENV['hooks'] = $hooks;
}

function plugin_get_model()
{
    $_models = [];
    $plugin_model_file = [];
    $plugin_dao_files = [];
    $plugin_field_files = [];
    $plugin_paths = plugin_paths_enabled();
    foreach ($plugin_paths as $path => $conf) {
        $dir = file_name($path);
        $models = glob(__PLUDIR__ . $dir . '/Model/*Model.php'); // path
        foreach ($models as $model) {
            $name = str_replace([__PLUDIR__ . $dir . '/Model/', 'Model.php'], '', $model);
            $plugin_model_file[$name] = $model;
        }
        $models = glob(__PLUDIR__ . $dir . '/Model/*/*Model.php'); // path
        foreach ($models as $model) {
            $name = str_replace([dirname($model) . "/", 'Model.php'], '', $model);
            $plugin_model_file[$name] = $model;
        }
        $models = glob(__PLUDIR__ . $dir . '/Dao/*Dao.php'); // path
        foreach ($models as $model) {
            $name = str_replace([__PLUDIR__ . $dir . '/Dao/', 'Dao.php'], '', $model);
            $_models[] = $name;
            $plugin_dao_files[$name] = $model;
        }
        $models = glob(__PLUDIR__ . $dir . '/Dao/*/*Dao.php'); // path
        foreach ($models as $model) {
            $name = str_replace([dirname($model) . "/", 'Dao.php'], '', $model);
            $_models[] = $name;
            $plugin_dao_files[$name] = $model;
        }
        $models = glob(__PLUDIR__ . $dir . '/Field/*.php'); // path
        foreach ($models as $model) {
            $name = str_replace([__PLUDIR__ . $dir . '/Field/', 'Field.php'], '', $model);
            $plugin_field_files[$name] = $model;
        }
        $models = glob(__PLUDIR__ . $dir . '/Field/*/*.php'); // path
        foreach ($models as $model) {
            $name = str_replace([dirname($model) . "/", 'Field.php'], '', $model);
            $plugin_field_files[$name] = $model;
        }
    }

    $_ENV['plugin_model_files'] = $plugin_model_file;
    $_ENV['plugin_dao_files'] = $plugin_dao_files;
    $_ENV['plugin_field_files'] = $plugin_field_files;
    return $_models;
}

//初始化自动加载
function plugin_get_autoload()
{
    $plugin_paths = plugin_paths_enabled();
    foreach ($plugin_paths as $path => $pconf) {
        $dir = file_name($path);
        foreach (scanPath(__PLUDIR__ . $dir . "/autoload.php") as $file) {
            require_once $file;
        }
    }
}

function plugin_get_controller()
{

    $plugin_auto_file = [];
    $plugin_controllers_file = [];
    $plugin_top_controllers_file = [];
    $plugin_task_controllers_file = [];
    $plugin_top_task_controllers_file = [];
    $plugin_view_file = [];
    $plugin_paths = plugin_paths_enabled();

    foreach ($plugin_paths as $path => $pconf) {
        $dir = file_name($path);
        foreach (scanPath(__PLUDIR__ . $dir . "/Controller/*", [], 'php') as $file) {
            $name = str_replace([__PLUDIR__ . $dir . '/Controller/', '.php'], '', $file);
            $name = str_replace('/', '\\', $name);
            $top = explode('\\', $name);
            if (!isset($top[1])) {
                $plugin_top_controllers_file[$name] = $file;
            } else {
                $plugin_controllers_file[$name] = $file;
            }
        }

        foreach (scanPath(__PLUDIR__ . $dir . "/Controller/*", [], 'html') as $file) {
            $name = str_replace(__PLUDIR__ . $dir . '/Controller/', '', $file);
            $plugin_view_file[$name] = $file;
        }

        foreach (scanPath(__PLUDIR__ . $dir . "/Task/*", [], 'php') as $file) {
            $name = str_replace([__PLUDIR__ . $dir . '/Task/', '.php'], '', $file);
            $name = str_replace('/', '\\', $name);
            $top = explode('\\', $name);
            if (!isset($top[1])) {
                $plugin_top_task_controllers_file[$name] = $file;
            } else {
                $plugin_task_controllers_file["Task\\" . $name] = $file;
            }
        }
    }

    $_ENV['plugin_top_controllers_file'] = $plugin_top_controllers_file;
    $_ENV['plugin_controllers_files'] = $plugin_controllers_file;
    $_ENV['plugin_top_task_controllers_file'] = $plugin_top_task_controllers_file;
    $_ENV['plugin_task_controllers_files'] = $plugin_task_controllers_file;
    $_ENV['plugin_view_files'] = $plugin_view_file;
    $_ENV['plugin_auto_file'] = $plugin_auto_file;
}

function compile_inc_callback($m)
{
    $hookname = ltrim($m[1], '@');
    $path = $hookname;
    if ($hookname != $m[1]) {
        $path = __APPDIR__ . 'Inc/' . $hookname;
    } else {
        $hookname = ltrim($m[1], '#');
        $hookname != $m[1] && $path = __APPDIR__ . 'controller/' . $hookname;
    }
    return is_file($path) ? file_read($path) : '/*' . $path . '*/';
}


function plugin_compile_srcfile_callback($m)
{
    $hooks = $_ENV['hooks'];
    $s = '';
    $hookname = $m[1];
    if (!empty($hooks[$hookname])) {
        $fileext = file_ext($hookname);
        foreach ($hooks[$hookname] as $path) {
            $t = file_read($path);
            if ($fileext == 'php' && preg_match('#^\s*<\?php\s+exit;#is', $t)) {
                // 正则表达式去除兼容性比较好。
                $t = preg_replace('#^\s*<\?php\s*exit;(.*?)(?:\?>)?\s*$#is', '\\1', $t);
            }

            substr($t, 0, 5) == '<?php' && $t = substr($t, 6);

            $s .= $t;
        }
    }
    return $s;
}

function plugin_compile_makefile_callback($m)
{
    $model = $m[2];

    $s='';
    if (!empty($_ENV['sys']['Join'][$model])) {
        switch ($m[1]) {
            case 'list_data':
                foreach ($_ENV['sys']['Join'][$model] as $k=> $row) {
                    $s .= "     \${$k}s = [];\r\n";
                }
                $s.='       foreach ($data[\'data\'] as $k=> $row){';
                $s.="\r\n";
                    foreach ($_ENV['sys']['Join'][$model] as $k=> $row) {
                        $s .= "     !empty(\$row['$k']) && \${$k}s[] = \$row['$k'];\r\n";
                    }
                $s.='       }';
                $s.="\r\n";
                foreach ($_ENV['sys']['Join'][$model] as $k => $row) {
                    $table_key = explode('.', $row);
                    $s .= "   \${$k}Data =!empty(\${$k}s) ? \$this->$table_key[0]->select(['{$table_key[1]}'=>\${$k}s],[],0,0,'*','$table_key[1]'):[];\r\n";
                }
                break;
            case 'list_foreach':
                $s='       foreach ($data[\'data\'] as $k=> &$row){';
                $s.="\r\n";
                foreach ($_ENV['sys']['Join'][$model] as $k => $row) {
                    $table_key = explode('.', $row);
                    $s .= "   \$row['{$k}Data'] = \${$k}Data[\$row['$k']] ?? [];\r\n";
                }
                $s .='       }';
                break;
            case 'read_data':

                break;
            case 'add_data':

                break;
            case 'edit_data':

                break;
        }
    }
    return $s;
}


function plugin_compile_replace_callback($m)
{

    $s = '';
    switch ($m[1]) {
        case 'controller':
            $s .= 'switch ($controller) {';
            foreach ($_ENV['_controller_name'] as $value) {
                $s .= "\r\n                  case '{$value["C"]}':\r\n                       \$app = new \\app\\{$value["C"]}();";
                $s .= "\r\n                       \$this->initializeController(\$app, \$controller, \$route, \$request, \$response, \$ctrl, \$action);\r\n";
                $ss = '                         $end = match ($action) {';
                foreach ($value as $action => $row) {
                    if ($action != 'C') {
                        $ss .= "\r\n                             '{$action}'=> \$app->action_{$action}(),";
                    }
                }

                $ss = rtrim($ss, ',');
                $ss .= "\r\n                          };\r\n";
                $s .= $ss . '                       break;';
            }
            $s = rtrim($s, ',');
            $s .= "\r\n           };";

            break;
        case 'taks_controller':
            $s .= '$app = match ($ctrl."\\\\".$action) {';
            foreach ($_ENV['_taks_controller_name'] as $k => $value) {
                foreach ($value as $action => $row) {
                    $s .= "\r\n                  '{$k}\\{$action}'=> (new {$k}())->{$action}(\$r['data']),";
                }
            }
            $s = rtrim($s, ',');
            $s .= "\r\n                  };";
            break;
        case 'table':
            $s .= 'return match ($name) {';
            foreach ($_ENV['Table'] as $key => $value) {
                $s .= "\r\n             '{$key}'=> \$_ENV['Table']['{$key}'],";
            }
            $s = rtrim($s, ',');
            $s .= "\r\n      };";
            break;
        case 'swoole_table':
            foreach ($_ENV['Table'] as $key => $value) {
                $s .= "\r\n             \$this->T_{$key} = \$_ENV['Table']['{$key}'];";
            }
            break;
            /*
        case 'swoole_table_attr':
            foreach ($_ENV['table'] as $key => $value) {
                $s .= "\r\n             \$this->T_{$key} = \$_ENV['table']['{$key}'];";
            }
            break;
            */
        case 'swoole_table_public':
            foreach ($_ENV['Table'] as $key => $value) {
                $s .= "\r\n   public Table   \$T_{$key};";
            }
            break;

        case 'models':
            $s .= 'return match ($name) {';
            foreach ($_ENV['_models_name'] as $key => $value) {
                $s .= "\r\n             '{$key}'=> \$_ENV['_models']['{$key}'],";
            }
            $s .= "\r\n             default=> throw new CustomException(\$name . 'Model禁止访问', 500),";
            $s = rtrim($s, ',');
            $s .= "\r\n      };";
            break;
    }
    return $s;
}

// 安装，卸载，禁用，更新
function plugin_read_by_dir($dir, $local, $local_first = TRUE)
{
//    $plugins = $_ENV['plugins'];
//    $local = array_value($plugins, $dir, array());
//    if (empty($local)) return array();
    // 本地插件信息
    !isset($local['rank']) && $local['rank'] = 0;
    !isset($local['name']) && $local['name'] = '';
    !isset($local['price']) && $local['price'] = 0;
    !isset($local['brief']) && $local['brief'] = '';
    !isset($local['version']) && $local['version'] = '1.0.0.0';
    !isset($local['zx_version']) && $local['zx_version'] = '1.0.0.0';
    !isset($local['installed']) && $local['installed'] = 0;
    !isset($local['enable']) && $local['enable'] = 0;
    !isset($local['hooks']) && $local['hooks'] = [];
    !isset($local['hooks_rank']) && $local['hooks_rank'] = [];
    !isset($local['dependencies']) && $local['dependencies'] = [];
    !isset($local['install_url']) && $local['install_url'] = '';
    $local['version_int'] = ToInt($local['version']);
    $local['zx_version_int'] = ToInt($local['zx_version']);
    // 额外的判断
    $local['dir'] = $dir;
    return $local;
}

?>